package zhoufu.sz.sanxiao_check.utils;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.media.ExifInterface;
import android.net.Uri;
import android.util.Log;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;

/**
 * User:RCP
 * email:ruancaipu@gmail.com
 * Date:2016/3/4
 */
public class VolleyImageUtils
{
    public static final String TAG = VolleyImageUtils.class.getSimpleName();

    /**
     * get scaled bitmap
     *
     * @param filePath local file path
     * @param maxWidth scaled bitmap width you desired, if maxWidth < maxHeight, then scaled
     * bitmap width is maxWidth while bitmap height is maxWidth * ratio
     * @param maxHeight scaled bitmap height you desired, if maxHeight < maxWidth, then scaled
     * bitmap height is maxHeight while bitmap width is maxHeight / ratio.
     * @return scaled bitmap
     */
    public static Bitmap getScaledBitmap(String filePath, int maxWidth, int maxHeight)
    {
        BitmapFactory.Options decodeOptions = new BitmapFactory.Options();
        Bitmap                bitmap;
        // If we have to resize this image, first get the natural bounds.
        decodeOptions.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(filePath, decodeOptions);
        int actualWidth  = decodeOptions.outWidth;
        int actualHeight = decodeOptions.outHeight;
        Log.d(TAG, "Actual width: " + actualWidth + ", actual height: " + actualHeight);
        // Then compute the dimensions we would ideally like to decode to.
        int desiredWidth = getResizedDimension(maxWidth, maxHeight,
                actualWidth, actualHeight);
        int desiredHeight = getResizedDimension(maxHeight, maxWidth,
                actualHeight, actualWidth);
        Log.d(TAG, "Desired width: " + desiredWidth + ", desired height: " + desiredHeight);

        // Decode to the nearest power of two scaling factor.
        decodeOptions.inJustDecodeBounds = false;
        // TODO(ficus): Do we need this or is it okay since API 8 doesn't support it?
        // decodeOptions.inPreferQualityOverSpeed = PREFER_QUALITY_OVER_SPEED;
        decodeOptions.inSampleSize =
                findBestSampleSize(actualWidth, actualHeight, desiredWidth, desiredHeight);
        Bitmap tempBitmap = BitmapFactory.decodeFile(filePath, decodeOptions);
        // If necessary, scale down to the maximal acceptable size.
        if ( tempBitmap != null && ( tempBitmap.getWidth() > desiredWidth ||
                tempBitmap.getHeight() > desiredHeight ) )
        {
            bitmap = Bitmap.createScaledBitmap(tempBitmap,
                    desiredWidth, desiredHeight, true);
            tempBitmap.recycle();
        } else
        {
            bitmap = tempBitmap;
        }
        return bitmap;
    }

    /**
     * get scaled bitmap
     *
     * @param imageResId image resource id
     * @param maxWidth scaled bitmap width you desired, if maxWidth < maxHeight, then scaled
     * bitmap width is maxWidth while bitmap height is maxWidth * ratio
     * @param maxHeight scaled bitmap height you desired, if maxHeight < maxWidth, then scaled
     * bitmap height is maxHeight while bitmap width is maxHeight / ratio.
     * @return scaled bitmap
     */
    public static Bitmap getScaledBitmap(Context context, int imageResId, int maxWidth,
            int maxHeight)
    {
        BitmapFactory.Options decodeOptions = new BitmapFactory.Options();
        Bitmap                bitmap        = null;
        // If we have to resize this image, first get the natural bounds.
        decodeOptions.inJustDecodeBounds = true;
        BitmapFactory.decodeResource(context.getResources(), imageResId, decodeOptions);
        int actualWidth  = decodeOptions.outWidth;
        int actualHeight = decodeOptions.outHeight;
        Log.d(TAG, "Actual width: " + actualWidth + ", actual height: " + actualHeight);

        // Then compute the dimensions we would ideally like to decode to.
        int desiredWidth = getResizedDimension(maxWidth, maxHeight,
                actualWidth, actualHeight);
        int desiredHeight = getResizedDimension(maxHeight, maxWidth,
                actualHeight, actualWidth);
        Log.d(TAG, "Desired width: " + desiredWidth + ", desired height: " + desiredHeight);

        // Decode to the nearest power of two scaling factor.
        decodeOptions.inJustDecodeBounds = false;
        // TODO(ficus): Do we need this or is it okay since API 8 doesn't support it?
        // decodeOptions.inPreferQualityOverSpeed = PREFER_QUALITY_OVER_SPEED;
        decodeOptions.inSampleSize =
                findBestSampleSize(actualWidth, actualHeight, desiredWidth, desiredHeight);
        Bitmap tempBitmap = BitmapFactory
                .decodeResource(context.getResources(), imageResId, decodeOptions);
        // If necessary, scale down to the maximal acceptable size.
        if ( tempBitmap != null && ( tempBitmap.getWidth() > desiredWidth ||
                tempBitmap.getHeight() > desiredHeight ) )
        {
            bitmap = Bitmap.createScaledBitmap(tempBitmap,
                    desiredWidth, desiredHeight, true);
            tempBitmap.recycle();
        } else
        {
            bitmap = tempBitmap;
        }
        return bitmap;
    }

    /**
     * Returns the largest power-of-two divisor for use in downscaling a bitmap
     * that will not result in the scaling past the desired dimensions.
     *
     * @param actualWidth Actual width of the bitmap
     * @param actualHeight Actual height of the bitmap
     * @param desiredWidth Desired width of the bitmap
     * @param desiredHeight Desired height of the bitmap
     */
    // Visible for testing.
    private static int findBestSampleSize(
            int actualWidth, int actualHeight, int desiredWidth, int desiredHeight)
    {
        double wr    = (double) actualWidth / desiredWidth;
        double hr    = (double) actualHeight / desiredHeight;
        double ratio = Math.min(wr, hr);
        float  n     = 1.0f;
        while (( n * 2 ) <= ratio)
        {
            n *= 2;
        }

        return (int) n;
    }

    /**
     * Scales one side of a rectangle to fit aspect ratio.
     *
     * @param maxPrimary Maximum size of the primary dimension (i.e. width for
     * max width), or zero to maintain aspect ratio with secondary
     * dimension
     * @param maxSecondary Maximum size of the secondary dimension, or zero to
     * maintain aspect ratio with primary dimension
     * @param actualPrimary Actual size of the primary dimension
     * @param actualSecondary Actual size of the secondary dimension
     */
    private static int getResizedDimension(int maxPrimary, int maxSecondary, int actualPrimary,
            int actualSecondary)
    {
        // If no dominant value at all, just return the actual.
        if ( maxPrimary == 0 && maxSecondary == 0 )
        {
            return actualPrimary;
        }

        // If primary is unspecified, scale primary to match secondary's scaling ratio.
        if ( maxPrimary == 0 )
        {
            double ratio = (double) maxSecondary / (double) actualSecondary;
            return (int) ( actualPrimary * ratio );
        }

        if ( maxSecondary == 0 )
        {
            return maxPrimary;
        }

        double ratio   = (double) actualSecondary / (double) actualPrimary;
        int    resized = maxPrimary;
        if ( resized * ratio > maxSecondary )
        {
            resized = (int) ( maxSecondary / ratio );
        }
        return resized;
    }

    /**
     * get actual image dimension
     *
     * @param imagePath local file path
     */
    public static int[] getActualImageDimension(String imagePath)
    {
        int[]                 imageSize     = new int[2];
        BitmapFactory.Options decodeOptions = new BitmapFactory.Options();
        // If we have to resize this image, first get the natural bounds.
        decodeOptions.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(imagePath, decodeOptions);
        int actualWidth  = decodeOptions.outWidth;
        int actualHeight = decodeOptions.outHeight;
        imageSize[0] = actualWidth;
        imageSize[1] = actualHeight;
        return imageSize;
    }

    /**
     * get actual image dimension
     *
     * @param imageResId image resource id
     */
    public static int[] getActualImageDimension(Context context, int imageResId)
    {
        int[]                 imageSize     = new int[2];
        BitmapFactory.Options decodeOptions = new BitmapFactory.Options();
        // If we have to resize this image, first get the natural bounds.
        decodeOptions.inJustDecodeBounds = true;
        BitmapFactory.decodeResource(context.getResources(), imageResId, decodeOptions);
        int actualWidth  = decodeOptions.outWidth;
        int actualHeight = decodeOptions.outHeight;
        imageSize[0] = actualWidth;
        imageSize[1] = actualHeight;
        return imageSize;
    }

    private static int[] getDesiredImageDimension(String imagePath, int maxWidth, int maxHeight)
    {
        int[] desiredImageDimension = new int[2];
        int[] actualImageDimension  = getActualImageDimension(imagePath);
        Log.d(TAG, "Actual width: " + actualImageDimension[0] + ", actual height: "
                + actualImageDimension[1]);
        int maxPrimary;
        int maxSecondary;
        if ( actualImageDimension[0] >= actualImageDimension[1] )
        {
            maxPrimary = maxWidth;
            maxSecondary = 0;
            desiredImageDimension[0] = getResizedDimension(maxPrimary, maxSecondary,
                    actualImageDimension[0], actualImageDimension[1]);
            desiredImageDimension[1] = getResizedDimension(maxSecondary, maxPrimary,
                    actualImageDimension[1], actualImageDimension[0]);
        } else
        {
            maxPrimary = maxHeight;
            maxSecondary = 0;
            desiredImageDimension[1] = getResizedDimension(maxPrimary, maxSecondary,
                    actualImageDimension[1], actualImageDimension[0]);
            desiredImageDimension[0] = getResizedDimension(maxPrimary, maxSecondary,
                    actualImageDimension[0], actualImageDimension[1]);
        }
        Log.d(TAG, "Desired width: " + desiredImageDimension[0] + ", desired height: "
                + desiredImageDimension[1]);
        return desiredImageDimension;
    }

    private static int[] getDesiredImageDimension(Context context, int imageResId, int maxWidth,
            int maxHeight)
    {
        int[] desiredImageDimension = new int[2];
        int[] actualImageDimension  = getActualImageDimension(context, imageResId);
        Log.d(TAG, "Actual width: " + actualImageDimension[0] + ", actual height: "
                + actualImageDimension[1]);
        int maxPrimary;
        int maxSecondary;
        if ( actualImageDimension[0] >= actualImageDimension[1] )
        {
            maxPrimary = maxWidth;
            maxSecondary = 0;
            desiredImageDimension[0] = getResizedDimension(maxPrimary, maxSecondary,
                    actualImageDimension[0], actualImageDimension[1]);
            desiredImageDimension[1] = getResizedDimension(maxSecondary, maxPrimary,
                    actualImageDimension[1], actualImageDimension[0]);
        } else
        {
            maxPrimary = maxHeight;
            maxSecondary = 0;
            desiredImageDimension[1] = getResizedDimension(maxPrimary, maxSecondary,
                    actualImageDimension[1], actualImageDimension[0]);
            desiredImageDimension[0] = getResizedDimension(maxPrimary, maxSecondary,
                    actualImageDimension[0], actualImageDimension[1]);
        }
        Log.d(TAG, "Desired width: " + desiredImageDimension[0] + ", desired height: "
                + desiredImageDimension[1]);
        return desiredImageDimension;
    }

    /**
     * compress the image file, create a scaled compressed image file, and overwrite the origin
     * one.
     *
     * @param path origin image file path
     */
    public static void compress(String path, int maxWidth, int maxHeight, int quality)
    {
        FileOutputStream out;
        try
        {
            Bitmap scaledBitmap  = getScaledBitmap(path, maxWidth, maxHeight);
            Bitmap rotatedBitmap = rotateBitmap(getBitmapDegree(path), scaledBitmap);
            out = new FileOutputStream(path);
            Bitmap mutableBitmap = rotatedBitmap.copy(Bitmap.Config.ARGB_8888, true);

            // write the compressed bitmap at the destination specified by filename.
            mutableBitmap.compress(Bitmap.CompressFormat.JPEG, quality, out);
        } catch (FileNotFoundException e)
        {
            e.printStackTrace();
        }
    }

    /**
     * compress the image file, create a scaled compressed image file, and overwrite the origin
     * one.
     */
    public static void compress(String originPath, String outputPath, int maxWidth, int maxHeight,
            int quality)
    {
        FileOutputStream out;
        try
        {
            Bitmap scaledBitmap  = getScaledBitmap(originPath, maxWidth, maxHeight);
            Bitmap rotatedBitmap = rotateBitmap(getBitmapDegree(originPath), scaledBitmap);
            out = new FileOutputStream(outputPath);
            Bitmap mutableBitmap = rotatedBitmap.copy(Bitmap.Config.ARGB_8888, true);

            // write the compressed bitmap at the destination specified by filename.
            mutableBitmap.compress(Bitmap.CompressFormat.JPEG, quality, out);
        } catch (FileNotFoundException e)
        {
            e.printStackTrace();
        }
    }

    /**
     * add water mark at the left top of image.
     *
     * @param srcPath local image file path
     * @param watermarkRes watermark resource
     * @param maxWidth scaled bitmap width you desired, if maxWidth < maxHeight, then scaled
     * bitmap width is maxWidth while bitmap height is maxWidth * ratio
     * @param maxHeight scaled bitmap height you desired, if maxHeight < maxWidth, then scaled
     * bitmap height is maxHeight while bitmap width is maxHeight / ratio.
     * @param quality compress quality
     */
    // http://developer.android.com/training/displaying-bitmaps/load-bitmap.html
    public static void watermark(Context context, String srcPath, int watermarkRes, int maxWidth,
            int maxHeight, int quality)
    {

        FileOutputStream out;
        try
        {
            Bitmap scaledBitmap    = getScaledBitmap(srcPath, maxWidth, maxHeight);
            Bitmap rotatedBitmap   = rotateBitmap(getBitmapDegree(srcPath), scaledBitmap);
            Bitmap scaledWatermark = getScaledBitmap(context, watermarkRes, maxWidth, maxHeight);
            out = new FileOutputStream(srcPath);

            Bitmap mutableBitmap = rotatedBitmap.copy(Bitmap.Config.ARGB_8888, true);
            Canvas canvas        = new Canvas(mutableBitmap);
            canvas.drawBitmap(scaledWatermark, 0, 0, null);

            // write the compressed bitmap at the destination specified by filename.
            mutableBitmap.compress(Bitmap.CompressFormat.JPEG, quality, out);
        } catch (FileNotFoundException e)
        {
            e.printStackTrace();
        } catch (OutOfMemoryError e)
        {
            Log.e(TAG, "Add watermark result in OOM");
            e.printStackTrace();
        }
    }

    /**
     * add watermark at the right bottom of the image
     *
     * @param srcPath local image file path
     * @param watermarkRes watermark resource
     * @param maxWidth scaled bitmap width you desired, if maxWidth < maxHeight, then scaled
     * bitmap width is maxWidth while bitmap height is maxWidth * ratio
     * @param maxHeight scaled bitmap height you desired, if maxHeight < maxWidth, then scaled
     * bitmap height is maxHeight while bitmap width is maxHeight / ratio.
     * @param quality compress quality
     */
    public static void watermarkAtRightBottom(Context context, String srcPath, int watermarkRes,
            int maxWidth, int maxHeight, int quality)
    {

        FileOutputStream out;
        try
        {
            Bitmap scaledBitmap    = getScaledBitmap(srcPath, maxWidth, maxHeight);
            Bitmap rotatedBitmap   = rotateBitmap(getBitmapDegree(srcPath), scaledBitmap);
            Bitmap scaledWatermark = getScaledBitmap(context, watermarkRes, maxWidth, maxHeight);
            out = new FileOutputStream(srcPath);

            int left = rotatedBitmap.getWidth() - scaledWatermark.getWidth();
            int top  = rotatedBitmap.getHeight() - scaledWatermark.getHeight();

            Bitmap mutableBitmap = rotatedBitmap.copy(Bitmap.Config.ARGB_8888, true);
            Canvas canvas        = new Canvas(mutableBitmap);
            canvas.drawBitmap(scaledWatermark, left, top, null);

            // write the compressed bitmap at the destination specified by filename.
            mutableBitmap.compress(Bitmap.CompressFormat.JPEG, quality, out);
        } catch (FileNotFoundException e)
        {
            e.printStackTrace();
        } catch (OutOfMemoryError e)
        {
            Log.e(TAG, "Add watermark result in OOM");
            e.printStackTrace();
        }
    }

    /**
     * get bitmap degree, you may get an rotated photo when you take a picture in some devices.
     *
     * @param path local image file path
     */
    public static int getBitmapDegree(String path)
    {
        int degree = 0;
        try
        {
            ExifInterface exifInterface = new ExifInterface(path);
            int orientation = exifInterface
                    .getAttributeInt(ExifInterface.TAG_ORIENTATION,
                            ExifInterface.ORIENTATION_NORMAL);
            switch (orientation)
            {
                case ExifInterface.ORIENTATION_ROTATE_90:
                    degree = 90;
                    break;
                case ExifInterface.ORIENTATION_ROTATE_180:
                    degree = 180;
                    break;
                case ExifInterface.ORIENTATION_ROTATE_270:
                    degree = 270;
                    break;
            }
        } catch (IOException e)
        {
            e.printStackTrace();
        }
        return degree;
    }

    /**
     * rotate bitmap
     *
     * @param angle rotate angle
     * @param bitmap origin bitmap
     * @return rotated bitmap
     */
    public static Bitmap rotateBitmap(int angle, Bitmap bitmap)
    {
        Matrix matrix = new Matrix();
        matrix.postRotate(angle);
        try
        {
            return Bitmap.createBitmap(bitmap, 0, 0,
                    bitmap.getWidth(), bitmap.getHeight(), matrix, true);
        } catch (Exception e)
        {
            return bitmap;
        }
    }

    /**
     * create a copied image.
     *
     * @param photoUri origin image uri
     * @param outputPath output image uri.
     * @return true if successfully compressed to the specified stream.
     * @throws IOException
     */
    public static boolean copyBitmapFile(Context context, Uri photoUri, String outputPath)
            throws IOException
    {
        // Load image from path
        InputStream input = context.getContentResolver().openInputStream(photoUri);

        // compress it
        Bitmap bitmapOrigin = BitmapFactory.decodeStream(input);
        if ( bitmapOrigin == null )
        {
            return false;
        }
        // save to file
        FileOutputStream output = new FileOutputStream(outputPath);
        return bitmapOrigin.compress(Bitmap.CompressFormat.JPEG, 100, output);
    }
}
